﻿// -- MVC4WPF
// -- Copyright © 2009 Information Control Corporation
// -- Revision History:
// -- Version 0.9.0.0 First codeplex release  - Stephen Giffin

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EnvDTE;
using Microsoft.VisualStudio.TemplateWizard;
using MVC4WPF.Automation;
using MVC4WPF.Automation.Wizards;
using MVC4WPF.Automation.VisualStudio.Dialog;
using MVC4WPF.Automation.Data;
using System.Windows.Media.Imaging;
using System.Windows.Controls;
using System.Windows.Forms;

namespace MVC4WPF.Automation.VisualStudio.Wizards
{
    /// <summary>
    /// 
    /// </summary>
    public partial class MVC4WPFControllerWizard : MVC4WPFWizardBase
    {
        #region Class Logic

        /// <summary>
        /// Loads the base controller contract that will be used for filtering. This will throw an exception if it is not one of the accepted types.
        /// </summary>
        /// <param name="contractType">The type of base contract to load.</param>
        /// <returns></returns>
        private Type LoadControllerContractType(MVCItemType viewType)
        {
            Type contractBaseType = null;
            switch (viewType)
            {
                case MVCItemType.WindowController:
                    contractBaseType = typeof(IWPFControllerWindow);
                    break;
                case MVCItemType.PageController:
                    contractBaseType = typeof(IWPFControllerPage);
                    break;
                case MVCItemType.UserControlController:
                    contractBaseType = typeof(IWPFControllerUserControl);
                    break;
                default:
                    throw new ApplicationException("The ControllerType was not an expected value");
                    break;
            }

            return contractBaseType;
        }

        /// <summary>
        /// Loads the base view contract that will be used for filtering. This will throw an exception if it is not one of the accepted types.
        /// </summary>
        /// <param name="contractType">The type of base contract to load.</param>
        /// <returns></returns>
        private Type LoadViewContractType(MVCItemType viewType)
        {
            Type contractBaseType = null;
            switch (viewType)
            {
                case MVCItemType.WindowController:
                    contractBaseType = typeof(IWPFViewWindow);
                    break;
                case MVCItemType.PageController:
                    contractBaseType = typeof(IWPFViewPage);
                    break;
                case MVCItemType.UserControlController:
                    contractBaseType = typeof(IWPFViewUserControl);
                    break;
                default:
                    throw new ApplicationException("The ControllerType was not an expected value");
                    break;
            }

            return contractBaseType;
        }

        #endregion

        #region MVC4WPFWizardBase Members

        /// <summary>
        /// Override method to execute the wizard.
        /// </summary>
        /// <param name="automationObject">The automation coming in by default provided by the system.</param>
        /// <param name="replacementsDictionary">A string/string dictionary that represents the replacement parameters for a template</param>
        /// <param name="runKind">The kind of wizard run</param>
        /// <param name="customParams">Any custom parameters sent in to the template</param>
        public override void ExecuteWizard(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
        {
            if (this.CurrentProject != null)
            {
                //pull off the controller type
                MVCItemType controllerType = (MVCItemType)Enum.Parse(typeof(MVCItemType), replacementsDictionary[FormatDictionaryParameterName(WizardParameters.CONTROLLERTYPE)]);

                //get the location of the template files to process
                string templateVersion = replacementsDictionary[FormatDictionaryParameterName(WizardParameters.TEMPLATEVERSION)];

                //Using a helper method in the automation library to lookup the template folder based on the version of the template.
                string templateFilePath = Automation.TemplateManager.GetTemplateFilePath(templateVersion);

                IControllerDialog controllerDialog = new ControllerDialog();
                controllerDialog.ViewPropertyBag.AvailableControllerContracts = this.LoadContracts(LoadControllerContractType(controllerType), this.CurrentProject).OrderBy(x => x.Name).ThenBy(x => x.FullName).ToList();
                controllerDialog.ViewPropertyBag.AvailableViewContracts = this.LoadContracts(LoadViewContractType(controllerType), this.CurrentProject).OrderBy(x => x.Name).ThenBy(x => x.FullName).ToList();
                controllerDialog.ViewPropertyBag.ControllerContractsExist = controllerDialog.ViewPropertyBag.AvailableControllerContracts.Count > 0;
                controllerDialog.ViewPropertyBag.ControllerType = controllerType;
                controllerDialog.ViewPropertyBag.DialogTitle = replacementsDictionary[FormatDictionaryParameterName(WizardParameters.DIALOGTITLE)];
                controllerDialog.ViewPropertyBag.ItemName = replacementsDictionary[FormatDictionaryParameterName("rootname")];
                controllerDialog.ViewPropertyBag.SelectName = "Create Controller";
                controllerDialog.ViewPropertyBag.AvailableModelContracts = this.LoadContracts(typeof(IModel), this.CurrentProject).OrderBy(x => x.Name).ThenBy(x => x.FullName).ToList();
                controllerDialog.ViewPropertyBag.ModelContractsExist = controllerDialog.ViewPropertyBag.AvailableModelContracts.Count > 0;

                //show the dialog
                if (controllerDialog.ShowDialog() == false)
                {
                    //something happened to cancel the process. don't add item
                    this.ShouldAddItem = false;
                }
                else
                {
                    #region Logic for the Controller Template

                    //gather information about the selected view contract
                    ContractInformation viewContractInfo = ContractBuilder.BuildContract(controllerDialog.ViewPropertyBag.SelectedViewContract);

                    //add the view contract name to the parameters
                    replacementsDictionary.Add(FormatDictionaryParameterName(WizardParameters.VIEWCONTRACTNAME), viewContractInfo.FullName);

                    //pull off the relevant pieces of information for the view contract
                    List<ContractEvent> events = viewContractInfo.Events;

                    //grab the template locations
                    string subscribeToEventTemplatePath = String.Format(@"{0}\{1}", templateFilePath, replacementsDictionary[FormatDictionaryParameterName(WizardParameters.SUBSCRIBETOEVENTTEMPLATENAME)]);
                    string eventHandlerTemplatePath = String.Format(@"{0}\{1}", templateFilePath, replacementsDictionary[FormatDictionaryParameterName(WizardParameters.EVENTHANDLERTEMPLATENAME)]);
                    string releaseEventHandlerTemplatePath = String.Format(@"{0}\{1}", templateFilePath, replacementsDictionary[FormatDictionaryParameterName(WizardParameters.RELEASEEVENTHANDLERTEMPLATENAME)]);

                    //execute the templates
                    TransformResults subscribeToEventResults = this.ExecuteTemplate<List<ContractEvent>>(subscribeToEventTemplatePath, events);
                    TransformResults eventHandlerResults = this.ExecuteTemplate<List<ContractEvent>>(eventHandlerTemplatePath, events);
                    TransformResults releaseEventResults = this.ExecuteTemplate<List<ContractEvent>>(releaseEventHandlerTemplatePath, events);

                    //if any errors have occured, we need to bail
                    if (subscribeToEventResults.TransformErrors.HasErrors || eventHandlerResults.TransformErrors.HasErrors || releaseEventResults.TransformErrors.HasErrors)
                    {
                        MessageBox.Show(Properties.Resources.TemplateCompileErrors);
                        this.ShouldAddItem = false;
                        return;
                    }
                    else
                    {
                        //no errors, inject the corresponding parameters
                        replacementsDictionary.Add(FormatDictionaryParameterName(WizardParameters.SUBSCRIBETOEVENTTEMPLATECONTENT), subscribeToEventResults.Transformation);
                        replacementsDictionary.Add(FormatDictionaryParameterName(WizardParameters.EVENTHANDLERTEMPLATECONTENT), eventHandlerResults.Transformation);
                        replacementsDictionary.Add(FormatDictionaryParameterName(WizardParameters.RELEASEEVENTHANDLERTEMPLATECONTENT), releaseEventResults.Transformation);
                    }

                    #endregion

                    #region Logic for the Controller Contract Template

                    //by default, the results of all the template execution will be String.Empty, as will the contract name
                    string propertyTemplateResults = String.Empty;
                    string eventTemplateResults = String.Empty;
                    string methodTemplateResults = String.Empty;
                    string releasePropertyTemplateResults = String.Empty;
                    string releaseEventTemplateResults = String.Empty;
                    string controllerContractName = String.Empty;

                    //gather information about the controller contract to implement, if necessary
                    if (controllerDialog.ViewPropertyBag.ControllerUsesControllerContract)
                    {
                        //only if the controller uses a contract do any of these steps need to be taken care of
                        ContractInformation controllerContractInfo = ContractBuilder.BuildContract(controllerDialog.ViewPropertyBag.SelectedControllerContract);

                        controllerContractName = String.Format(", {0}", controllerContractInfo.FullName);

                        //pull off the relevant pieces of information
                        List<ContractProperty> properties = controllerContractInfo.Properties;
                        events = controllerContractInfo.Events; //already declared up top, just reusing
                        List<ContractMethod> methods = controllerContractInfo.Methods;

                        string propertyTemplatePath = String.Format(@"{0}\{1}", templateFilePath, replacementsDictionary[FormatDictionaryParameterName(WizardParameters.PROPERTYTEMPLATENAME)]);
                        string eventTemplatePath = String.Format(@"{0}\{1}", templateFilePath, replacementsDictionary[FormatDictionaryParameterName(WizardParameters.EVENTTEMPLATENAME)]);
                        string methodTemplatePath = String.Format(@"{0}\{1}", templateFilePath, replacementsDictionary[FormatDictionaryParameterName(WizardParameters.METHODTEMPLATENAME)]);
                        string releasePropertyTemplatePath = String.Format(@"{0}\{1}", templateFilePath, replacementsDictionary[FormatDictionaryParameterName(WizardParameters.RELEASEPROPERTYTEMPLATENAME)]);
                        string releaseEventTemplatePath = String.Format(@"{0}\{1}", templateFilePath, replacementsDictionary[FormatDictionaryParameterName(WizardParameters.RELEASEEVENTTEMPLATENAME)]);

                        //executing the templates and storing the return values
                        TransformResults propertyResults = this.ExecuteTemplate<List<ContractProperty>>(propertyTemplatePath, properties);
                        TransformResults eventResults = this.ExecuteTemplate<List<ContractEvent>>(eventTemplatePath, events);
                        TransformResults methodResults = this.ExecuteTemplate<List<ContractMethod>>(methodTemplatePath, methods);
                        TransformResults releasePropertyResults = this.ExecuteTemplate<List<ContractProperty>>(releasePropertyTemplatePath, properties);
                        TransformResults releaseEventHandlerResults = this.ExecuteTemplate<List<ContractEvent>>(releaseEventTemplatePath, events);

                        //if any errors have occured, we need to bail
                        if (propertyResults.TransformErrors.HasErrors || eventResults.TransformErrors.HasErrors || methodResults.TransformErrors.HasErrors || releasePropertyResults.TransformErrors.HasErrors || releaseEventHandlerResults.TransformErrors.HasErrors)
                        {
                            MessageBox.Show(Properties.Resources.TemplateCompileErrors);
                            this.ShouldAddItem = false;
                            return;
                        }
                        else
                        {
                            //no errors, set up the corresponding parameters   
                            propertyTemplateResults = propertyResults.Transformation;
                            eventTemplateResults = eventResults.Transformation;
                            methodTemplateResults = methodResults.Transformation;
                            releasePropertyTemplateResults = releasePropertyResults.Transformation;
                            releaseEventTemplateResults = releaseEventHandlerResults.Transformation;
                        }
                    }

                    //inject required parameters regardless of whether or not they're populated
                    replacementsDictionary.Add(FormatDictionaryParameterName(WizardParameters.PROPERTYTEMPLATECONTENT), propertyTemplateResults);
                    replacementsDictionary.Add(FormatDictionaryParameterName(WizardParameters.EVENTTEMPLATECONTENT), eventTemplateResults);
                    replacementsDictionary.Add(FormatDictionaryParameterName(WizardParameters.METHODTEMPLATECONTENT), methodTemplateResults);
                    replacementsDictionary.Add(FormatDictionaryParameterName(WizardParameters.RELEASEPROPERTYTEMPLATECONTENT), releasePropertyTemplateResults);
                    replacementsDictionary.Add(FormatDictionaryParameterName(WizardParameters.RELEASEEVENTTEMPLATECONTENT), releaseEventTemplateResults);
                    replacementsDictionary.Add(FormatDictionaryParameterName(WizardParameters.CONTROLLERCONTRACTNAME), controllerContractName);

                    #endregion

                    #region Logic for the Model Contract

                    //some default values for parameters should no model contract be used
                    string controllerModelContractName = String.Empty;
                    string controllerUsesModelContract = "False";
                    string modelContractName = string.Empty;

                    if (controllerDialog.ViewPropertyBag.ControllerUsesModelContract)
                    {
                        //only if the controller uses a model contract do any of these steps need to be taken care of
                        ContractInformation modelContractInfo = ContractBuilder.BuildContract(controllerDialog.ViewPropertyBag.SelectedModelContract);

                        controllerModelContractName = String.Format(", {0}<{1}>", MVC4WPFConstants.ControllerModelContractInterfaceName, modelContractInfo.FullName);
                        controllerUsesModelContract = "True";
                        modelContractName = modelContractInfo.FullName;

                        //pull off the relevant pieces of information for the model contract
                        events = modelContractInfo.Events;

                        string subscribeToModelEventTemplatePath = String.Format(@"{0}\{1}", templateFilePath, replacementsDictionary[FormatDictionaryParameterName(WizardParameters.SUBSCRIBETOMODELEVENTTEMPLATENAME)]);
                        string modelEventHandlerTemplatePath = String.Format(@"{0}\{1}", templateFilePath, replacementsDictionary[FormatDictionaryParameterName(WizardParameters.MODELEVENTHANDLERTEMPLATENAME)]);
                        string releaseModelEventHandlerTemplatePath = String.Format(@"{0}\{1}", templateFilePath, replacementsDictionary[FormatDictionaryParameterName(WizardParameters.RELEASEMODELEVENTHANDLERTEMPLATENAME)]);

                        TransformResults subscribeToModelEventResults = this.ExecuteTemplate<List<ContractEvent>>(subscribeToModelEventTemplatePath, events);
                        TransformResults modelEventHandlerResults = this.ExecuteTemplate<List<ContractEvent>>(modelEventHandlerTemplatePath, events);
                        TransformResults releaseModelEventResults = this.ExecuteTemplate<List<ContractEvent>>(releaseModelEventHandlerTemplatePath, events);

                        //if any errors have occured, we need to bail
                        if (subscribeToModelEventResults.TransformErrors.HasErrors || modelEventHandlerResults.TransformErrors.HasErrors || releaseModelEventResults.TransformErrors.HasErrors)
                        {
                            MessageBox.Show(Properties.Resources.TemplateCompileErrors);
                            this.ShouldAddItem = false;
                            return;
                        }
                        else
                        {
                            //no errors, inject the corresponding parameters
                            replacementsDictionary.Add(FormatDictionaryParameterName(WizardParameters.SUBSCRIBETOMODELEVENTTEMPLATECONTENT), subscribeToModelEventResults.Transformation);
                            replacementsDictionary.Add(FormatDictionaryParameterName(WizardParameters.MODELEVENTHANDLERTEMPLATECONTENT), modelEventHandlerResults.Transformation);
                            replacementsDictionary.Add(FormatDictionaryParameterName(WizardParameters.RELEASEMODELEVENTHANDLERTEMPLATECONTENT), releaseModelEventResults.Transformation);
                        }
                    }

                    //add the default parameters
                    replacementsDictionary.Add(FormatDictionaryParameterName(WizardParameters.CONTROLLERMODELCONTRACTNAME), controllerModelContractName);
                    replacementsDictionary.Add(FormatDictionaryParameterName(WizardParameters.CONTROLLERUSESMODELCONTRACT), controllerUsesModelContract);
                    replacementsDictionary.Add(FormatDictionaryParameterName(WizardParameters.MODELCONTRACTNAME), modelContractName);                    

                    #endregion
                }
            }
        }

        #endregion
    }
}
